package com.ksyun.seckill.service.impl.V7;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ksyun.seckill.constants.RedisKey;
import com.ksyun.seckill.constants.SecKillImplVer;
import com.ksyun.seckill.dto.CampaignCacheInfoDto;
import com.ksyun.seckill.dto.CampaignItemDto;
import com.ksyun.seckill.entity.Campaign;
import com.ksyun.seckill.mapper.CampaignMapper;
import com.ksyun.seckill.rest.RestResult;
import com.ksyun.seckill.service.CampaignService;
import com.ksyun.seckill.service.SkService;
import com.ksyun.seckill.util.ConfuseIdUtil;
import com.ksyun.seckill.util.exception.ServiceException;
import com.ksyun.seckill.vo.CampaignBasisInfoVO;
import com.ksyun.seckill.vo.CreateCampaignVO;
import com.ksyun.seckill.vo.SimpleCampaignVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.net.URLEncoder;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service
@Slf4j
public class DefaultCampaignServiceImplV7 extends ServiceImpl<CampaignMapper, Campaign> implements CampaignService, SkService {


    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    /**
     * 检验秒杀活动的合法性，如活动是不存在，活动是否允许参与秒杀等.
     *
     * @param campaignId 活动id.
     */
    @Override
    public void validateCampaign(Long campaignId) {

    }


    /**
     * 指定活动，用户是否已下过单，同一个手机号，同一个活动，只能参与一次.
     *
     * @param campaignId 活动id.
     * @param phone      手机号.
     * @return
     */
    @Override
    public Boolean haveOrder(Long campaignId, String phone) {
        return null;
    }


    /**
     * 秒杀.
     *
     * @param campaignId 活动id.
     * @param phone      手机号.
     * @param path       秒杀URL路径.
     */
    @Override
    public void secKill(Long campaignId, String phone, String path) {

    }

    /**
     * 服务实现版本.
     *
     * @return
     */
    @Override
    public SecKillImplVer getSecKillImplVer() {
        return SecKillImplVer.V7;
    }

    /**
     * 生成秒杀URL路径.
     *
     * @param campaignId 活动id.
     * @param phone      手机号.
     * @param verifyCode 验证码
     * @return
     */
    @Override
    public String generateSecKillPath(Long campaignId, String phone, String verifyCode) {
        return SkService.super.generateSecKillPath(campaignId, phone, verifyCode);
    }

    /**
     * 检查秒杀URL路径合法性.
     *
     * @param campaignId 活动id.
     * @param phone      手机号.
     * @param path       秒杀URL路径.
     * @return
     */
    @Override
    public boolean checkSecKillPath(Long campaignId, String phone, String path) {
        return SkService.super.checkSecKillPath(campaignId, phone, path);
    }


    /**
     * get请求，获取活动列表
     *
     * @return
     */
    @Override
    public List<CampaignItemDto> selectCampaignList() {
        List<Campaign> campaigns = this.baseMapper.selectList(null);
        List<CampaignItemDto> retList = new ArrayList<>();
        if (!ObjectUtils.isEmpty(campaigns)) {
            retList = campaigns.stream().map(item -> {
                return EntityToItemDto(item);
            }).collect(Collectors.toList());
        }
        return retList;
    }


    /**
     * 创建活动
     *
     * @param createCampaignVO
     */
    @Override
    public boolean saveCampaign(CreateCampaignVO createCampaignVO) {
        if (ObjectUtils.isEmpty(createCampaignVO)) {
            return false;
        }
        /**
         * 还需要判断对应的name在数据库中是否有同名的
         */
        QueryWrapper<Campaign> wrapper = new QueryWrapper<>();
        wrapper.eq(createCampaignVO.getName() != null, "name", createCampaignVO.getName());
        Campaign one = this.baseMapper.selectOne(wrapper);
        if (!ObjectUtils.isEmpty(one)) {
            //有重复活动名的
            throw new ServiceException("活动名重复!");
        }
        Campaign campaign = campaignVoToEntity(createCampaignVO);
        int insert = this.baseMapper.insert(campaign);
        return insert > 0;
    }

    /**
     * 上线某个活动
     *
     * 上线活动的时候将热点活动传入至redis中
     * @param campaignId
     * @return
     */
    @Override
    public RestResult onlineCampaign(Long campaignId) {
        if (ObjectUtils.isEmpty(campaignId)) {
            throw new ServiceException("传入的campaignId为空!");
        }
        Campaign campaign = this.baseMapper.selectById(campaignId);

        if (ObjectUtils.isEmpty(campaign)) {
            throw new ServiceException("不存在对应的活动");
        }
        //库存要大于0且，活动结束时间晚于当前时间
        if (campaign.getCurrentInventory() <= 0) {
            throw new ServiceException("活动库存不足，不能上线");
        }
        if (LocalDateTime.now().isAfter(campaign.getEndTime())) {
            throw new ServiceException("活动已经结束，不能上线");
        }
        //状态判断
        Integer status = campaign.getStatus();
        switch (status) {
            case 1:
                campaign.setStatus(2);
                campaign.setUpdateTime(LocalDateTime.now());
                this.baseMapper.updateById(campaign);
                //将数据传入至redis中
                CampaignCacheInfoDto cache = new CampaignCacheInfoDto();
                //将数据写成json字符串
                BeanUtils.copyProperties(campaign, cache);
                //将cache以hashmap的形式存储至redis
                Map<String, Object> objMap = BeanUtil.beanToMap(cache, new HashMap<>(),
                        CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> {
                            if(fieldName.equals("beginTime")||fieldName.equals("endTime")){
                                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                                LocalDateTime fieldValue1 = (LocalDateTime) fieldValue;
                                String format = fieldValue1.format(formatter);
                                return format;
                            }
                            return fieldValue.toString();

                        }));
                stringRedisTemplate.opsForHash().putAll(RedisKey.CAMPAIGN_INFO.formatKey(campaignId), objMap);
                return RestResult.success().msg("上线成功");

            case 2:
                throw new ServiceException("活动早已上线!");
            case 3:
                throw new ServiceException("活动已经结束");
            case 4:
                throw new ServiceException("活动已下线,不支持做上线操作");
            default:
                throw new ServiceException("status invalid!!! 上线操作失败！");
        }
    }

    /**
     * 下线某个活动
     *
     * @param campaignId
     * @return
     */
    @Override
    public RestResult offlineCampaign(Long campaignId) {
        if (ObjectUtils.isEmpty(campaignId)) {
            throw new ServiceException("传入的campaignId为空!");
        }
        Campaign campaign = this.baseMapper.selectById(campaignId);
        if (ObjectUtils.isEmpty(campaign)) {
            throw new ServiceException("不存在对应的活动");
        }
        Integer status = campaign.getStatus();
        switch (status) {
            case 1:
                throw new ServiceException("活动还未上线");
            case 2:
                campaign.setStatus(4);
                campaign.setUpdateTime(LocalDateTime.now());
                this.baseMapper.updateById(campaign);
                //下线某个活动的时候，将缓存从redis中删除掉
                stringRedisTemplate.delete(RedisKey.CAMPAIGN_INFO.formatKey(campaignId));
                return RestResult.success().msg("下线成功!");
            case 3:
                throw new ServiceException("活动已结束，不支持做下线操作");
            case 4:
                throw new ServiceException("活动已下线，不支持做下线操作");
            default:
                throw new ServiceException("status invalid!!!下线操作失败！");
        }
    }

    /**
     * 删除某个活动
     *
     * @param campaignId
     * @return
     */
    @Override
    public RestResult deleteCampaign(Long campaignId) {
        if (ObjectUtils.isEmpty(campaignId)) {
            throw new ServiceException("传入的campaignId为空!");
        }
        Campaign campaign = this.baseMapper.selectById(campaignId);
        if (ObjectUtils.isEmpty(campaign)) {
            throw new ServiceException("不存在对应的活动");
        }
        Integer status = campaign.getStatus();
        switch (status) {
            case 1:
                this.baseMapper.deleteById(campaignId);
                return RestResult.success().msg("删除成功!");
            case 2:
                throw new ServiceException("活动已上线，不支持做删除操作");
            case 3:
                this.baseMapper.deleteById(campaignId);
                return RestResult.success().msg("删除成功!");
            case 4:
                this.baseMapper.deleteById(campaignId);
                return RestResult.success().msg("删除成功!");
            default:
                throw new ServiceException("status invalid!!! 删除操作失败！");
        }
    }


    /**
     * 用户端查询活动列表
     * 要求返回的是混淆过活动id的列表
     *
     * @return
     */
    @Override
    public List<CampaignBasisInfoVO> selectSeckCampaignList() {
        /**
         *  每次进行查询的时候，对于数据库实体中，需要及时更新对应的status 当此时的时间 > endtime的时候，更新status为3。
         *
         *  TODO 后续列表可以进行redis进行优化加速查询
         */
        QueryWrapper<Campaign> wrapper = new QueryWrapper<>();
        wrapper.in("status", 2, 3);
        List<Campaign> campaigns = this.baseMapper.selectList(wrapper);
        int i = 0;
        for (Campaign campaign : campaigns) {
            if (LocalDateTime.now().isAfter(campaign.getEndTime()) || campaign.getCurrentInventory() <= 0) {
                //过期
                campaign.setStatus(3);
                i = 1;
            }
        }
        if (i == 1) {
            this.updateBatchById(campaigns);
        }
        //进行list集合元素的类型转化。
        List<CampaignBasisInfoVO> ret_list = campaigns.stream().map(item -> {
            CampaignBasisInfoVO target = EntityToCampaignInfoVo(item);
            return target;
        }).collect(Collectors.toList());
        return ret_list;
    }

    /**
     * 查询某个活动的详情信息
     *
     * @param campaignId
     * @return
     */
    @Override
    public SimpleCampaignVO getSimpleCampaignInfo(String campaignId) {
        if (StringUtils.isEmpty(campaignId)) {
            throw new ServiceException("campaignId不能为空");
        }
        try {
            //1、解析campaignId
            String decrypt = ConfuseIdUtil.decrypt(campaignId);
            Long decryptId = Long.valueOf(decrypt);
            //2、根据解析后的id查询对应的活动。 TODO 后续可以用redis进行优化,加快速度
            Campaign one = this.baseMapper.selectById(decryptId);
            if (ObjectUtils.isEmpty(one)) {
                throw new ServiceException("给定的campaignid无效!");
            }
            //3、将查询到的数据实体转化为vo
            SimpleCampaignVO campaignVO = campaignEntityToVo(one);
            //4、返回vo
            return campaignVO;
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 数据实体转化为vo
     *
     * @param
     * @return
     */
    private SimpleCampaignVO campaignEntityToVo(Campaign one) {
        try {
            SimpleCampaignVO campaignVO = new SimpleCampaignVO();
            campaignVO.setDescr(one.getDescr());
            campaignVO.setBeginTime(one.getBeginTime());
            campaignVO.setCurrentInventory(one.getCurrentInventory());
            campaignVO.setEndTime(one.getEndTime());
            campaignVO.setId(ConfuseIdUtil.encrypt(one.getId().toString()));
            campaignVO.setImgUrl(one.getImgUrl());
            campaignVO.setName(one.getName());
            campaignVO.setPlanInventory(one.getPlanInventory());
            if(one.getBeginTime().isBefore(LocalDateTime.now())){
                campaignVO.setRemainSeconds(0l);
            }else{
                Duration between = Duration.between(one.getBeginTime(), LocalDateTime.now());
                long seconds = Math.abs(between.getSeconds());
                campaignVO.setRemainSeconds(seconds);
            }
            String statusName;
            if(one.getStatus()!=2||LocalDateTime.now().isAfter(one.getEndTime())||one.getCurrentInventory()<=0){
                statusName= "已结束";
            }else if(LocalDateTime.now().isBefore(one.getBeginTime())){
                statusName="未开始";
            }else{
                statusName="已开始";
            }
            campaignVO.setPrettyStatusName(statusName);
            return campaignVO;
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 将活动实体转化为用户端活动vo
     * 要注意混淆id
     *
     * @param item
     * @return
     */
    private CampaignBasisInfoVO EntityToCampaignInfoVo(Campaign item) {
        CampaignBasisInfoVO target = new CampaignBasisInfoVO();
        try {

            //1、对id进行混淆加密
            String confuseID = ConfuseIdUtil.encrypt(item.getId().toString());
            // 混淆后还要进行编码，防止前端服务器会少传东西
            String encode = URLEncoder.encode(confuseID, "UTF-8");
            target.setId(encode);
            target.setName(item.getName());
            target.setImgUrl(item.getImgUrl());
            /**
             * 根据status判断此时的状态描述：未开始，已开始，已结束
             * status=2的需要判断此时的当前时间是否在开始时间之前或者之后，是开始和未开始
             * status=3的是已结束
             */
            String statusName;
            switch (item.getStatus()) {
                case 2:
                    if (LocalDateTime.now().isBefore(item.getBeginTime())) {
                        statusName = "未开始";
                    } else {
                        statusName = "已开始";
                    }
                    break;
                case 3:
                    statusName = "已结束";
                    break;
                default:
                    throw new ServiceException("状态未知异常!");
            }
            target.setPrettyStatusName(statusName);
            return target;
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }


    /**
     * 将活动vo转化为活动entity
     *
     * @param createCampaignVO
     * @return
     */
    private Campaign campaignVoToEntity(CreateCampaignVO createCampaignVO) {
        Campaign campaign = new Campaign();
        campaign.setUpdateTime(LocalDateTime.now());
        campaign.setStatus(1);
        campaign.setPlanInventory(createCampaignVO.getPlanInventory());
        campaign.setName(createCampaignVO.getName());
        campaign.setImgUrl(createCampaignVO.getImgUrl());
        campaign.setDescr(createCampaignVO.getDescr());
        campaign.setCurrentInventory(createCampaignVO.getCurrentInventory());
        campaign.setBeginTime(createCampaignVO.getBeginTime());
        campaign.setEndTime(createCampaignVO.getEndTime());
        campaign.setCreateTime(LocalDateTime.now());
        return campaign;
    }

    /**
     * 将活动实体转化为dto
     *
     * @param item
     * @return
     */
    private CampaignItemDto EntityToItemDto(Campaign item) {
        CampaignItemDto campaignItemDto = new CampaignItemDto();
        campaignItemDto.setUpdateTime(item.getUpdateTime());
        campaignItemDto.setBeginTime(item.getBeginTime());
        campaignItemDto.setCreateTime(item.getCreateTime());
        campaignItemDto.setDescr(item.getDescr());
        campaignItemDto.setName(item.getName());
        campaignItemDto.setCurrentInventory(item.getCurrentInventory());
        campaignItemDto.setEndTime(item.getEndTime());
        campaignItemDto.setId(item.getId());
        campaignItemDto.setImgUrl(item.getImgUrl());
        campaignItemDto.setPlanInventory(item.getPlanInventory());
        String statusName = "";
        switch (item.getStatus()) {
            case 1:
                statusName = "未上线";
                break;
            case 2:
                statusName = "已上线";
                break;
            case 3:
                statusName = "已下线";
                break;
            case 4:
                statusName = "已下线";
                break;
            default:
                throw new ServiceException("status Invalid!");
        }
        campaignItemDto.setStatusName(statusName);
        return campaignItemDto;
    }


}
